#include <protocolProcesser.h>

static int checkIsBoardcast(uint8_t *checkaddr)
{
    int tmpflag = 1;
    int i;
    for (i = 0; i < DEVICE_SERIAL_NUMBER_SIZE; i++)
    {
        if (checkaddr[i] != 0xff)
        {
            tmpflag = 0;
            break;
        }
    }
    return tmpflag;
}
static int checkIsMaster(struct protocolProcessorInform *processor)
{
    return checkIsBoardcast(processor->communicateInform.deviceSerialNumber);
}
static int deviceMatchAddress(uint8_t *myaddr, uint8_t *cmdaddr)
{
    int tmpflag = 0;
    int i;
    // check cmd is baordcast
    tmpflag = 0;
    if (checkIsBoardcast(cmdaddr))
    {
        return 1;
    }
    if (memcmp(myaddr, cmdaddr, 6) == 0)
    {
        // device match
        return 2;
    }
    // logwarn("myaddr=%s,cmdaddr=%s", getFixedHexBytesStr(myaddr, 6).byteStr, getFixedHexBytesStr(cmdaddr, 6).byteStr);
    return -1;
}
// protocol parse finish, and check
static void protocolParseProcessorCallback(void *packdata, uint32_t packsize, void *userdata)
{
    struct protocolProcessorInform *tmpProcesser = container_of(userdata, struct protocolProcessorInform, parser); //(struct protocolProcessorInform *)userdata;
    struct protocolPackageCommonInform *tmpprotocol = (struct protocolPackageCommonInform *)packdata;
    struct commandCommonStruct *tmpCommonCmd = (struct commandCommonStruct *)tmpprotocol->v3.v3Data;
    uint16_t tmpCommandID;
    uint32_t tmpResponseSize;
    int tmpflag = 0;
    int i;
    if (!tmpProcesser || !packdata)
    {
        logerror("");
        return;
    }

    if (tmpprotocol->protocolVersion != protocolParseCommonVersion3)
    {
        logerror("%d", tmpprotocol->protocolVersion);
        return;
    }
    if (!checkIsMaster(tmpProcesser))
    {
        if (deviceMatchAddress(tmpProcesser->communicateInform.deviceSerialNumber, tmpprotocol->v3.v3DesAddr) < 0)
        {
            logwarn("myaddr=%s,cmdaddr=%s", getMaxSizeHexStr(tmpProcesser->communicateInform.deviceSerialNumber).byteStr,
                    getMaxSizeHexStr(tmpprotocol->v3.v3DesAddr).byteStr);
            return;
        }
    }
    if (tmpprotocol->v3.v3CrypteType != protocolParseCrypte_None)
    {
        logerror("%d", tmpprotocol->v3.v3CrypteType);
        return;
    }
    if (tmpprotocol->v3.v3DataSize < PROTOCOL_COMMAND_COMMON_HEAD_SIZE)
    {
        logerror("parse:%d", tmpprotocol->v3.v3DataSize);
        return;
    }
    tmpCommandID = tmpCommonCmd->commandNB;
    tmpResponseSize = tmpprotocol->v3.v3DataSize - PROTOCOL_COMMAND_COMMON_HEAD_SIZE;
    // tmpCommandID = protocolParsePretreatCommon(tmpProcesser, packdata, packsize);
    tmpflag = hashCommandActuatorRun(&tmpProcesser->protocolCommandMng, tmpCommandID, tmpCommonCmd->commandData, &tmpResponseSize);

#if COMMAND_MASTER_CALL_DEFINE
    if (checkIsMaster(tmpProcesser))
    {
        if (memcmp(tmpProcesser->commandExcuterAddr, tmpprotocol->v3.v3DesAddr, 6) != 0)
        {
            logerror("myaddr=%s,cmdaddr=%s", getMaxSizeHexStr(tmpProcesser->commandExcuterAddr).byteStr,
                    getMaxSizeHexStr(tmpprotocol->v3.v3DesAddr).byteStr);
            return;
        }
        if (tmpCommonCmd->commandNB != tmpProcesser->commandExcuter.commandNB)
        {
            logerror("serial %d != %d", tmpCommonCmd->commandNB, tmpProcesser->commandExcuter.commandNB);
            return;
        }
        if (tmpCommonCmd->commandSN != tmpProcesser->commandExcuter.commandSN)
        {
            logerror("serial %d != %d", tmpCommonCmd->commandSN, tmpProcesser->commandExcuter.commandSN);
            return;
        }
        tmpProcesser->commandExcuter.responseFlag = tmpCommonCmd->responseFlag;
        memcpy(tmpProcesser->commandExcuterResponse, tmpCommonCmd->commandData, tmpprotocol->v3.v3DataSize - PROTOCOL_COMMAND_COMMON_HEAD_SIZE);
    }
    else
#endif
    {
        if (tmpflag < 0)
        {
            logerror("");
            return;
        }
        else if (tmpCommonCmd->commandNB == 0)
        {
        }
        else if (checkIsBoardcast(tmpprotocol->v3.v3DesAddr))
        {
            logdebug("not surpport broadcast");
            return;
        }
        tmpCommonCmd->responseFlag = tmpflag;
        tmpprotocol->v3.v3DataSize = PROTOCOL_V3_DATA_SIZE(tmpResponseSize);
        protocolCommandPackAndSend(tmpProcesser, packdata, PROTOCOL_PACK_MAX_SIZE);
    }
}

void protocolProcessorInit(struct protocolProcessorInform *devider, struct protocolCommandCommunicateInform setCommuni,
                           struct hashCommandActuatorManage setHashActor)
{
    int i;
    if (!devider)
    {
        logerror("");
        return;
    }
    if (!setHashActor.commandHash || setHashActor.hashSize < 1)
    {
        logerror("");
        return;
    }
    if (!setCommuni.receiveFunction || !setCommuni.sendFunction)
    {
        logerror("");
        return;
    }
    devider->communicateInform = setCommuni;
    devider->protocolCommandMng = setHashActor;
    logdebug("myaddr=%s, point=%p", getMaxSizeHexStr(devider->communicateInform.deviceSerialNumber).byteStr, devider);
    for (i = 0; i < setHashActor.hashSize; i++)
    {
        setHashActor.commandHash[i].userdata = devider;
    }
    protocolPackageParseInit(&devider->parser, protocolParseProcessorCallback, devider->parsePackBuffer, sizeof(devider->parsePackBuffer));
#if COMMAND_MASTER_CALL_DEFINE
    devider->commandExcuter = (struct commandCommonStruct){0};
#endif
}

void protocolProcessorRunReceive(struct protocolProcessorInform *devider)
{
    int tmpreadsize, i;
    uint8_t tmpreadbuffer[32];
    if (!devider)
    {
        logerror("");
        return;
    }
    if (!devider->communicateInform.receiveFunction)
    {
        logerror("");
        return;
    }
    do
    {
        tmpreadsize = devider->communicateInform.receiveFunction(tmpreadbuffer, sizeof(tmpreadbuffer), devider->communicateInform.userdata);
        if (!(tmpreadsize > 0 && tmpreadsize <= sizeof(tmpreadbuffer)))
        {
            // logdebug("%d", tmpreadsize);
            break;
        }
        // logdebug("addr=%s, read size=%d", getMaxSizeHexStr(devider->communicateInform.deviceSerialNumber).byteStr, tmpreadsize);
        for (i = 0; i < tmpreadsize; i++)
        {
            protocolPackageParseValue(&devider->parser, tmpreadbuffer[i]);
        }
    } while (1);
}
uint16_t processerSendProtocolData(struct protocolProcessorInform *processer, uint8_t *outpack, uint32_t outsize)
{
    if (!processer || !outpack)
    {
        logerror("");
        return 0;
    }
    if (!processer->communicateInform.sendFunction)
    {
        logerror("");
        return 0;
    }
    return processer->communicateInform.sendFunction(outpack, outsize, processer->communicateInform.userdata);
}
uint16_t processerPackageProtocolData(struct protocolProcessorInform *processer, uint8_t *outpack, uint32_t outsize)
{
    struct protocolPackageCommonInform *tmppack = (struct protocolPackageCommonInform *)outpack;
    if (!processer || !outpack)
    {
        logerror("");
        return 0;
    }
    if ((PROTOCOL_V3_COMMAND_HEAD_SIZE + tmppack->v3.v3DataSize) > outsize)
    {
        logerror("data big:%d", tmppack->v3.v3DataSize);
        return 0;
    }
    // logdebug("%d", processer->communicateInform.my485Addr);
#if COMMAND_MASTER_CALL_DEFINE
    if(checkIsMaster(processer))
    {
        memcpy(tmppack->v3.v3DesAddr, processer->commandExcuterAddr, sizeof(tmppack->v3.v3DesAddr));
    }
#endif
    tmppack->v3.v3CrypteType = protocolParseCrypte_None;
    tmppack->v3.v3Remain1 = 0;
    tmppack->v3.v3Remain2 = 0;
    // setcommand->addr485 = processer->communicateInform.my485Addr;
    return protocolPackageDataV3(outpack, outsize);
}
uint16_t protocolCommandPackAndSend(struct protocolProcessorInform *processer, uint8_t *outpack, uint32_t outsize)
{
    uint16_t tmpPackSize = processerPackageProtocolData(processer, outpack, outsize);
    if (tmpPackSize < 1)
    {
        logerror("");
        return 0;
    }
    return processerSendProtocolData(processer, outpack, tmpPackSize);
}
#if COMMAND_MASTER_CALL_DEFINE
uint16_t generateCommandSerial()
{
    static uint16_t genseial = 0;
    return genseial++;
}
int masterProcesserSetReTryCount(struct protocolProcessorInform *processer, uint8_t setRetry)
{
    if (!processer)
    {
        logerror("");
        return -1;
    }
    processer->communicateInform.retryMaxTime = LIMIT_VAL(setRetry, 0, 5);
    return 0;
}
int masterProcesserSetTimeout(struct protocolProcessorInform *processer, uint32_t setTimeoutMs)
{
    if (!processer)
    {
        logerror("");
        return -1;
    }
    processer->communicateInform.commandWaitTimeoutMs = LIMIT_VAL(setTimeoutMs, PROTOCOL_MIN_WAIT_TIMEOUT_MS, PROTOCOL_MAX_WAIT_TIMEOUT_MS);
    return 0;
}
int masterCommandProcesserState(struct protocolProcessorInform *devider)
{
    if (!devider)
    {
        logerror("");
        return commandExcuteResultUnkown;
    }
    return (int)(int8_t)devider->commandExcuter.responseFlag;
}
int masterCommandProcesserWaitResponse(struct protocolProcessorInform *processer)
{
    timerMsDefBegin(tmptimer);
    if (!processer)
    {
        logerror("");
        return commandExcuteResultUnkown;
    }
    processer->communicateInform.commandWaitTimeoutMs = LIMIT_VAL(processer->communicateInform.commandWaitTimeoutMs,
                                                                  PROTOCOL_MIN_WAIT_TIMEOUT_MS, PROTOCOL_MAX_WAIT_TIMEOUT_MS);
    do
    {
        protocolProcessorRunReceive(processer);
        if (masterCommandProcesserState(processer) != commandExcuteResultUnkown)
        {
            break;
        }
        usleep(10 * 1000);
    } while (timerMsRunMs(tmptimer) < processer->communicateInform.commandWaitTimeoutMs);
    return masterCommandProcesserState(processer);
}
int masterProcesserSendAndWaitResponse(struct protocolProcessorInform *processer, void *sendData, uint32_t sendSize)
{
    int tmpflag = 0;
    int8_t tmpRetryCount = 0;
    if (!processer)
    {
        logerror("");
        return commandExcuteResultUnkown;
    }
    processer->communicateInform.retryMaxTime = LIMIT_VAL(processer->communicateInform.retryMaxTime, 0, 5);
    do
    {
        processerSendProtocolData(processer, sendData, sendSize);
        tmpflag = masterCommandProcesserWaitResponse(processer);
        if (tmpflag != commandExcuteResultUnkown)
        {
            break;
        }
    } while (tmpRetryCount++ < processer->communicateInform.retryMaxTime);
    return tmpflag;
}
#endif